/*
 * Title:		walker.g
 * Description: Contains the Walker of the KFrog interpreter. 
 * 
 * @version		1.0 May 2009
 * @author		Rami Habas
 */

header {
    package kfrog;
}

{
    import java.util.Vector;
    import java.util.ArrayList;
    import kfrog.type.*;
}

class KFrogAntlrWalker extends TreeParser;

options{
	importVocab = KFrog;
}

{
    private KFrogInterpreter interpreter = new KFrogInterpreter();
}

extern_def
{
    KFrogDataType retType;
    KFrogDataType[] argList;
}
        :#(MAIN_DEF name:"main" argList=arg_list mbody:.)
         {	retType = new KFrogVoid();
         	interpreter.registerFunction(name.getText(),argList,#mbody,retType);
         }
        |#(FUNC_DEF retType=type fname:ID argList=arg_list fbody:.)
         {interpreter.registerFunction(fname.getText(),argList,#fbody,retType);}
        | decl:DECLARATION          { declaration(#decl);}
        | #(PROGRAM (extd:.         { extern_def(#extd); })*)
        ;
        
main_func
        : . { interpreter.invokeFunction(this,new KFrogDataType[0],"main");
        	  interpreter.activateFrogs();}
        ;
        
type returns [ KFrogDataType dataType ]
{
    dataType = null;
}
        : "void"       	{ dataType = new KFrogVoid();}
        | #(TYPE
            ( "logical"	{ dataType = new KFrogLogical();}
            | "decimal"	{ dataType = new KFrogDecimal();}
            | "real"	{ dataType = new KFrogReal();}
            )
          )
        ;

arg_list returns [ KFrogDataType[] argList ]
{
    KFrogDataType argument;
    ArrayList<KFrogDataType> list;
    
    argList = null;
}
        : #(ARG_LIST        { list = new ArrayList<KFrogDataType>();}
            (argument=type arg:ID
            { argument.setName(arg.getText()); list.add(argument);}
            )*
          )                 { argList = KFrogInterpreter.getArray(list);}
        ;

declaration
{
    KFrogDataType argType;
    KFrogDataType argument = null;
}
        : #(DECLARATION argType=type
            ( var:ID (#(ASSIGN argument=expression))?
              { interpreter.registerVariable(var.getText(), argType, argument);
                argument = null;
              }
            )*
          )
        ;

statement returns [ KFrogDataType r ]
{
    KFrogDataType a;
    KFrogDataType b;
    r = null;
}
        :decl:DECLARATION { declaration(#decl);}
        | #("new" frogstmt:.)
          {
              interpreter.createFrog();
              r = statement(#frogstmt);
              interpreter.setFrogInactive();
          }
        | CLEAR
          {
              interpreter.clearPond();
          }
        | RESET
          {
              interpreter.resetPond();
          }
        | #(PONDSIZE a=expression b=expression)
          { 
          	  interpreter.createPond(a,b);
          }
        | #(PONDCOLOR pcolor:ID)
          { 
          	  interpreter.setPondColor(pcolor.getText());
          }
        | #(FROGCOLOR fcolor:ID)
          { 
          	  interpreter.setFrogColor(fcolor.getText());
          }
        | STARTDRAW
          {
              interpreter.startFrogDraw();
          }
        | STOPDRAW
          {
              interpreter.stopFrogDraw();
          }
        | #(SPEED a=expression)
          { 
          	  interpreter.setFrogSpeed(a);
          }
        | #(SLEEP a=expression)
          { 
          	  interpreter.setFrogSleep(a);
          }
        | CENTER
          {
              interpreter.setFrogCenter();
          }
        | #(GOTO a=expression b=expression)
          { 
          	  interpreter.setFrogXY(a,b);
          }
        | #(FORWARD a=expression)
          { 
          	  interpreter.moveFrogForward(a);
          }
        | #(TURNRIGHT a=expression)
          { 
          	  interpreter.turnFrogRight(a);
          }        
        | #(TURNLEFT a=expression)
          { 
          	  interpreter.turnFrogLeft(a);
          }                
        | #(RIGHTCURVE a=expression b=expression)
          { 
          	  interpreter.curveFrogRight(a,b);
          }
        | #(LEFTCURVE a=expression b=expression)
          { 
          	  interpreter.curveFrogLeft(a,b);
          }        
        | #("if" a=expression thenp:. (elsep:.)?)
          {
              if (!(a instanceof KFrogLogical))
                  throw new RuntimeException("if condition must evaluate to " +
                                             "logical");
              KFrogInterpreter.validateIfStmt(#thenp);
              if (elsep != null)
                  KFrogInterpreter.validateIfStmt(#elsep);
              if (((KFrogLogical)a).getVariable())
                  r = statement(#thenp);
              else if (null != elsep)
                  r = statement(#elsep);
          }
        | #("while" wcond:. wbody:.)
          {
              KFrogInterpreter.validateIfStmt(#wbody);
              while (interpreter.loopMayProceed(this, #wcond)) {
                  r = statement(#wbody);
                  interpreter.handleContinue();
              }
              interpreter.handleBreak();
          }
        | #("repeat" a=expression rbody:.)
          {
              interpreter.executeRepeatLoop(this, a, #rbody);
          }        
        | "break"           { interpreter.setBreak(); }
        | "continue"        { interpreter.setContinue(); }
        | #("return"
            (a=expression   { r = KFrogInterpreter.rValue(a); })?
          )                 { interpreter.setReturn(); }
        | a=expr:expression { KFrogInterpreter.validateIfExpr(#expr); }
        | #(EXPR_LIST
            ( lexpr:.
            {
                KFrogInterpreter.validateIfExpr(#lexpr);
                expression(#lexpr);
            }
            )*
          )
        | #(STATEMENT
            ( stmt:.
            {
                KFrogInterpreter.validateIfStmtOrDeclaration(#stmt);
                if (interpreter.mayProceed())
                    r = statement(#stmt);
            }
            )*
          )
        | #(block:COMPOUND_STMT
          {
              #block.setType(STATEMENT);
              if (interpreter.mayProceed())
                  r = interpreter.executeBlock(this, #block);
              #block.setType(COMPOUND_STMT);
          }
          )
        ;
        
expr_list returns [ KFrogDataType[] eList ]
{
    KFrogDataType a;
    ArrayList<KFrogDataType> list;
    
    eList = null;
}
        : #(EXPR_LIST     { list = new ArrayList<KFrogDataType>();}
            (a=expression { list.add(a);}
            )*
          )				  { eList = KFrogInterpreter.getArray(list);}
        ;
        
expression returns [ KFrogDataType r ]
{
    KFrogDataType a, b;
    KFrogDataType[] exprList;
    
    r = null;
}
        : #(ASSIGN    a=expression b=expression) { r = a.assign(b);}
        | #(PLUSEQ    a=expression b=expression) { r = a.plusAssign(b);}
        | #(MINUSEQ   a=expression b=expression) { r = a.minusAssign(b);}
        | #(MULTEQ    a=expression b=expression) { r = a.timesAssign(b);}
        | #(DIVEQ     a=expression b=expression) { r = a.divideAssign(b);}
        | #(MODEQ	  a=expression b=expression) { r = a.moduloAssign(b);}
        | #(OR        a=expression b=expression) { r = a.or(b);}
        | #(AND       a=expression b=expression) { r = a.and(b);}
        | #(ISEQ      a=expression b=expression) { r = a.isEqual(b);}
        | #(NOTEQ     a=expression b=expression) { r = a.notEqual(b);}
        | #(LT        a=expression b=expression) { r = a.lessThan(b);}
        | #(GT        a=expression b=expression) { r = a.greaterThan(b);}
        | #(LTEQ      a=expression b=expression) { r = a.lessThanOrEqual(b);}
        | #(GTEQ      a=expression b=expression) { r = a.greaterThanOrEqual(b);}
        | #(PLUS      a=expression b=expression) { r = a.plus(b);}        
        | #(MINUS     a=expression b=expression) { r = a.minus(b);}
        | #(MULT      a=expression b=expression) { r = a.times(b);}
        | #(DIV       a=expression b=expression) { r = a.divide(b);}
        | #(MOD       a=expression b=expression) { r = a.modulo(b);}
        | #(PREF_INC  a=expression)              { r = a.prefixIncrement();}
        | #(PREF_DEC  a=expression)              { r = a.prefixDecrement();}
        | #(POST_INC  a=expression)              { r = a.postfixIncrement();}
        | #(POST_DEC  a=expression)              { r = a.postfixDecrement();}
        | #(UPLUS     a=expression)              { r = a.unaryPlus(); }
        | #(UMINUS    a=expression)              { r = a.unaryMinus(); }
        | #(NOT       a=expression)              { r = a.not(); }
		| i:DECIMAL	{ r = new KFrogDecimal(Integer.parseInt(i.getText()));}
        | d:REAL	{ r = new KFrogReal(Double.parseDouble(d.getText()));}
        | "true"	{ r = new KFrogLogical(true);}
        | "false"	{ r = new KFrogLogical(false);}
        | id:ID		{ r = interpreter.getVariable(id.getText());}
        | #(FUNC_CALL fname:ID exprList=expr_list)
          { r = interpreter.invokeFunction(this, exprList, fname.getText());}
        ;

